﻿Imports System.ComponentModel

''' <summary>
''' 冷凝实时工程数据
''' </summary>
''' <remarks></remarks>
Public Class CcapRealData

    ''' <summary>
    ''' 每个设备上每次读取数据的字节数
    ''' </summary>
    ''' <remarks></remarks>
    Private _ByteCountPerDevice As UShort = 116

    ''' <summary>
    ''' 每一次读取ModBus中的数据的设备数
    ''' </summary>
    ''' <remarks></remarks>
    Private _CountPerRead As UShort = 2

    ''' <summary>
    ''' 工程包含冷凝相关设备的数量
    ''' </summary>
    ''' <remarks></remarks>
    Private _DevCount As UShort

    ''' <summary>
    ''' 定义工程数据读取最大连接失败次数
    ''' </summary>
    ''' <remarks></remarks>
    Private Const MAX_COMFAIL_COUNT As Integer = 3

    ''' <summary>
    ''' 是否正在进行刷新数据操作
    ''' </summary>
    ''' <remarks>
    ''' UI界面是否正在进行刷新界面
    ''' </remarks>
    Public RefreshProcessing As Boolean

    ''' <summary>
    ''' 读取数据失败次数
    ''' </summary>
    ''' <remarks></remarks>
    Private _FailCount As Integer = 0

    ''' <summary>
    ''' 新数据处理事件
    ''' </summary>
    ''' <remarks></remarks>
    Public Event NewDataEvent As EventHandler

    ''' <summary>
    ''' 接收数据长度
    ''' </summary>
    ''' <remarks></remarks>
    Private _DataLength As UShort

    ''' <summary>
    ''' 数据长度
    ''' </summary>
    ''' <remarks></remarks>
    Public ReadOnly Property DataLength() As UShort
        Get
            Return _DataLength
        End Get
    End Property

    ''' <summary>
    ''' 接收数据时的功能代码
    ''' </summary>
    ''' <remarks></remarks>
    Private _FunctionCode As Byte

    ''' <summary>
    ''' 功能代码
    ''' </summary>
    ''' <remarks></remarks>
    Public ReadOnly Property FuncCode() As Byte
        Get
            Return _FunctionCode
        End Get
    End Property

    ''' <summary>
    ''' 设备ID号
    ''' </summary>
    ''' <remarks></remarks>
    Private _NetId As Byte = 255

    ''' <summary>
    ''' 设备ID号
    ''' </summary>
    ''' <remarks></remarks>
    Public ReadOnly Property NetId() As Byte
        Get
            Return _NetId
        End Get
    End Property

    ''' <summary>
    ''' 偏移地址
    ''' </summary>
    ''' <remarks></remarks>
    Private _Offset As UShort

    ''' <summary>
    ''' 偏移地址
    ''' </summary>
    ''' <remarks></remarks>
    Public ReadOnly Property Offset() As UShort
        Get
            Return _Offset
        End Get
    End Property

    ''' <summary>
    ''' 接收到的实时数据
    ''' </summary>
    ''' <remarks></remarks>
    Private _RealDataList As IList(Of CcapDataItem)
    ''' <summary>
    ''' 获取实时数据
    ''' </summary>
    ''' <remarks></remarks>
    Public ReadOnly Property RealDataList() As IList(Of CcapDataItem)
        Get
            Return _RealDataList
        End Get
    End Property

    ''' <summary>
    ''' 后台异步处理线程
    ''' </summary>
    ''' <remarks></remarks>
    Private WithEvents _BackWorker As BackgroundWorker

    ''' <summary>
    ''' 工程编号
    ''' </summary>
    ''' <remarks></remarks>
    Private ProjectId As String

    ''' <summary>
    ''' 使用设备ID进行初始
    ''' </summary>
    ''' <param name="projectId">项目编号</param>
    ''' <remarks></remarks>
    Public Sub New(ByVal projectId As String)
        Me._Offset = 21000
        Me.ProjectId = projectId
        Me._FunctionCode = FunctionCode.WordReadonly
        Me._DevCount = CcapDevItemService.GetNetDevicuesCount(Me.ProjectId)
        Me._DataLength = Me._DevCount * Me._ByteCountPerDevice


        Me.RefreshProcessing = False
        _RealDataList = New List(Of CcapDataItem)
        Me._BackWorker = New BackgroundWorker()
        Me._BackWorker.WorkerSupportsCancellation = True
        AddHandler Me._BackWorker.DoWork, AddressOf DataTransfer
    End Sub

    ''' <summary>
    ''' 开始传输数据
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub StartDataTransfer()
        _BackWorker.RunWorkerAsync()
    End Sub

    ''' <summary>
    ''' 停止传输
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub StopDataTransfer()
        _BackWorker.CancelAsync()
        Application.DoEvents()
    End Sub

    ''' <summary>
    ''' 传输数据
    ''' </summary>   
    ''' <remarks></remarks>
    Private Sub DataTransfer(ByVal sender As Object, ByVal e As DoWorkEventArgs) Handles _BackWorker.DoWork
        Threading.Thread.Sleep(1)


        While Not Me._BackWorker.CancellationPending
            '' 离线或者刷新时不执行获取数据
            If (g_offline OrElse Me.RefreshProcessing) Then
                Threading.Thread.Sleep(200)
                Continue While
            End If

            '' 刷新数据
            RefreshData()
            Threading.Thread.Sleep(g_ProjectDataRefresh_Interval)
        End While
    End Sub

    ''' <summary>
    ''' 获取实时数据的状态
    ''' </summary>
    ''' <remarks></remarks>
    Public Shared GetRealDataStatus As Boolean = False

    ''' <summary>
    ''' 刷新实时数据
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub RefreshData()
        '' 正在进行参数设置
        If g_busy Then
            Exit Sub
        End If

        ''==================================
        '' 可能会死锁
        '' 需要新定义一个变量

        GetRealDataStatus = True
        Dim readLine As Integer
        Dim m_readfail As Boolean = False
        Dim b(3) As Byte
        Dim buffer() As Byte = Nothing
        Try
            ''清除以前数据
            Me._RealDataList.Clear()

            For i As Integer = 0 To Me._DevCount - 1 Step Me._CountPerRead
                buffer = Nothing

                If (i + Me._CountPerRead) > Me._DevCount Then
                    readLine = Me._ByteCountPerDevice * (Me._DevCount - i)
                Else
                    readLine = Me._ByteCountPerDevice * Me._CountPerRead
                End If

                ''===============================
                '' 读取数据
                buffer = COMHelper.Instance.Send4Byte(_NetId, FuncCode, _Offset + i * Me._ByteCountPerDevice / 2, readLine / 2)

                If buffer Is Nothing Then
                    Continue For
                End If

                m_readfail = True
                ''================================
                ''  分解数据
                For j As Integer = 0 To buffer.Length - 1 Step _ByteCountPerDevice
                    Dim rd As CcapDataItem = New CcapDataItem()
                    '' Ex板的网络ID，必须连续。    
                    rd.BordId = i + 50 + (j / _ByteCountPerDevice)

                    '' 设置DI状态
                    For k As Integer = 0 To 1
                        rd.DIStatus(k) = buffer(j + k)
                        rd.DIStatusEnable(k) = buffer(j + k + 2)
                        rd.DIPreAlarm(k) = buffer(j + k + 4)
                        rd.DIAlarm(k) = buffer(j + k + 6)
                        rd.DIAlarmEnable(k) = buffer(j + k + 8)
                    Next k
                    ''===========================
                    '' 支持24DI
                    For k As Integer = 0 To 1
                        rd.DIStatus(k + 2) = buffer(j + k + 10)
                        rd.DIStatusEnable(k + 2) = buffer(j + k + 12)
                        rd.DIPreAlarm(k + 2) = buffer(j + k + 14)
                        rd.DIAlarm(k + 2) = buffer(j + k + 16)
                        rd.DIAlarmEnable(k + 2) = buffer(j + k + 18)
                    Next k

                    ''=================================
                    ''AI 端口
                    ''预警
                    rd.AILowPreAlarm(0) = buffer(j + 18 * 2 + 1)
                    rd.AIHighPreAlarm(0) = buffer(j + 18 * 2)

                    rd.AILowPreAlarm(1) = buffer(j + 20 * 2 + 1)
                    rd.AIHighPreAlarm(1) = buffer(j + 20 * 2)
                    '' 报警
                    rd.AILowAlarm(0) = buffer(j + 19 * 2 + 1)
                    rd.AIHighAlarm(0) = buffer(j + 19 * 2)

                    rd.AILowAlarm(1) = buffer(j + 21 * 2 + 1)
                    rd.AIHighAlarm(1) = buffer(j + 21 * 2)
                    ''状态
                    rd.AIValueEnable(0) = buffer(j + 22 * 2)
                    rd.AIValueEnable(1) = buffer(j + 22 * 2 + 1)

                    rd.AIAlarmEnable(0) = buffer(j + 23 * 2)
                    rd.AIAlarmEnable(1) = buffer(j + 23 * 2 + 1)

                    '' AI Vales
                    For k As Integer = 0 To 15
                        For w As Integer = 0 To 3
                            b(w) = buffer(j + 26 * 2 + k * 4 + w)
                        Next
                        rd.AIValue(k) = NumericConvert.Byte2Single(b)
                    Next
                    '' 添加到实时数据列表
                    Me._RealDataList.Add(rd)
                Next j
            Next i
        Catch ex As Exception
            Debug.Print(ex.Message)
        End Try

        GetRealDataStatus = False
        ''===============================
        '' 当未配置工程信息时，
        '' 触发新建数据事件
        If _DevCount > 0 Then '当未配置工程信息时不提示无法读取数据
            If m_readfail = False Then
                _FailCount += 1
                If _FailCount >= MAX_COMFAIL_COUNT Then
                    RaiseEvent NewDataEvent(Me, Nothing)
                End If
                Exit Sub
            End If
        End If

        _FailCount = 0
        RaiseEvent NewDataEvent(Me, New EventArgs())
    End Sub
End Class


''' <summary>
''' 冷凝数据传输类
''' </summary>
''' <remarks></remarks>
Public Class CcapDataItem

    ''' <summary>
    ''' Ex板编号NETID
    ''' </summary>
    ''' <remarks></remarks>
    Public BordId As Integer

    ''' <summary>
    ''' DI 状态
    ''' </summary>
    ''' <remarks></remarks>
    Public DIStatus(3) As Byte

    ''' <summary>
    ''' DI 状态Enable
    ''' </summary>
    ''' <remarks></remarks>
    Public DIStatusEnable(3) As Byte
    ''' <summary>
    ''' DI 预警
    ''' </summary>
    ''' <remarks></remarks>
    Public DIPreAlarm(3) As Byte


    ''' <summary>
    ''' DI报警
    ''' </summary>
    ''' <remarks></remarks>
    Public DIAlarm(3) As Byte

    ''' <summary>
    ''' DI报警Enable
    ''' </summary>
    ''' <remarks></remarks>
    Public DIAlarmEnable(3) As Byte

    ''' <summary>
    ''' AI 值
    ''' </summary>
    ''' <remarks></remarks>
    Public AIValue(15) As Single
    ''' <summary>
    ''' AI 低压预警
    ''' </summary>
    ''' <remarks></remarks>
    Public AILowPreAlarm(1) As Byte

    ''' <summary>
    ''' AI 高压预警
    ''' </summary>
    ''' <remarks></remarks>
    Public AIHighPreAlarm(1) As Byte

    ''' <summary>
    ''' AI低压报警
    ''' </summary>
    ''' <remarks></remarks>
    Public AILowAlarm(1) As Byte

    ''' <summary>
    ''' AI高压报警
    ''' </summary>
    ''' <remarks></remarks>
    Public AIHighAlarm(1) As Byte

    ''' <summary>
    ''' AI报警或预警Enable
    ''' </summary>
    ''' <remarks></remarks>
    Public AIAlarmEnable(1) As Byte

    ''' <summary>
    ''' AI Value Enable
    ''' </summary>
    ''' <remarks></remarks>
    Public AIValueEnable(1) As Byte

    ''' <summary>
    ''' 获取指定端口状态的激活
    ''' </summary>
    ''' <param name="portId">端口号</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function GetPortStatusEnable(ByVal portId As Integer) As Boolean

        If (portId > 24) Then
            portId = portId - 24
            Return IsTrue(Me.DIStatusEnable(2), portId)
        End If


        If (portId > 16) Then
            portId = portId - 16
            Return IsTrue(Me.DIStatusEnable(3), portId)
        End If


        If (portId > 8) Then
            portId = portId - 8
            Return IsTrue(Me.DIStatusEnable(0), portId)
        End If

        Return IsTrue(Me.DIStatusEnable(1), portId)
    End Function

    ''' <summary>
    ''' 检测btye指定为的值是否为0
    ''' </summary>
    ''' <returns>如果为0 返回true；否则返回False</returns>
    ''' <remarks></remarks>
    Private Function IsTrue(ByRef bt As Byte, ByVal index As Integer) As Boolean
        Return (bt And 2 ^ (index - 1)) = 0
    End Function

    ''' <summary>
    ''' 获取指定设备的对应端口状态
    ''' </summary>
    ''' <param name="portId">端口号</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function GetPortStatus(ByVal portId As Integer) As PortStatuEnums

        If (Not GetPortStatusEnable(portId)) Then
            Return PortStatuEnums.Unknown
        End If

        If (portId > 24) Then
            portId = portId - 24
            If (IsTrue(Me.DIStatus(2), portId)) Then
                Return PortStatuEnums.Open
            Else
                Return PortStatuEnums.Close
            End If
        End If

        If (portId > 16) Then
            portId = portId - 16
            If IsTrue(Me.DIStatus(3), portId) Then
                Return PortStatuEnums.Open
            Else
                Return PortStatuEnums.Close
            End If
        End If

        If (portId > 8) Then
            portId = portId - 8
            If (IsTrue(Me.DIStatus(0), portId)) Then
                Return PortStatuEnums.Open
            Else
                Return PortStatuEnums.Close
            End If
        End If

        If IsTrue(Me.DIStatus(1), portId) Then
            Return PortStatuEnums.Open
        Else
            Return PortStatuEnums.Close
        End If

    End Function


    ''' <summary>
    ''' 获取指定端口的预警或报警有效状态
    ''' </summary>
    ''' <param name="portId">端口号</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function GetPortAlarmEnable(ByVal portId As Integer) As Boolean

        If (portId > 24) Then
            portId = portId - 24
            Return IsTrue(Me.DIAlarmEnable(2), portId)
        End If

        If (portId > 16) Then
            portId = portId - 16
            Return IsTrue(Me.DIAlarmEnable(3), portId)
        End If

        If (portId > 8) Then
            portId = portId - 8
            Return IsTrue(Me.DIAlarmEnable(0), portId)
        End If

        Return IsTrue(Me.DIAlarmEnable(1), portId)

    End Function

    ''' <summary>
    ''' 获取指定端口的报警状态
    ''' </summary>
    ''' <param name="portId">端口号</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function GetPortAlarm(ByVal portId As Integer) As PortAlarmEnums
        If (Not GetPortAlarmEnable(portId)) Then
            Return PortAlarmEnums.Unknown
        End If

        If (portId > 24) Then
            portId = portId - 24
            If (IsTrue(Me.DIAlarm(2), portId)) Then
                Return PortAlarmEnums.Alarm
            ElseIf (IsTrue(Me.DIPreAlarm(2), portId)) Then
                Return PortAlarmEnums.PreAlarm
            Else
                Return PortAlarmEnums.Normal
            End If
        End If

        If (portId > 16) Then
            portId = portId - 16
            If (IsTrue(Me.DIAlarm(3), portId)) Then
                Return PortAlarmEnums.Alarm
            ElseIf (IsTrue(Me.DIPreAlarm(3), portId)) Then
                Return PortAlarmEnums.PreAlarm
            Else
                Return PortAlarmEnums.Normal
            End If
        End If

        If (portId > 8) Then
            portId = portId - 8
            If (IsTrue(Me.DIAlarm(0), portId)) Then
                Return PortAlarmEnums.Alarm
            ElseIf (IsTrue(Me.DIPreAlarm(0), portId)) Then
                Return PortAlarmEnums.PreAlarm
            Else
                Return PortAlarmEnums.Normal
            End If
        End If

        If (IsTrue(Me.DIAlarm(1), portId)) Then
            Return PortAlarmEnums.Alarm
        ElseIf (IsTrue(Me.DIPreAlarm(1), portId)) Then
            Return PortAlarmEnums.PreAlarm
        Else
            Return PortAlarmEnums.Normal
        End If


    End Function

    ''' <summary>
    ''' 获取AI端口值是否有效
    ''' </summary>
    ''' <param name="portId">端口编号</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function GetAIValueEnable(ByVal portId As Integer) As Boolean
        If (portId > 8) Then
            portId = portId - 8
            Return IsTrue(Me.AIValueEnable(0), portId)
        Else
            Return IsTrue(Me.AIValueEnable(1), portId)
        End If
    End Function

    ''' <summary>
    ''' 获取指定AI端口的值
    ''' </summary>
    ''' <param name="portId">端口编号</param>
    ''' <remarks></remarks>
    Public Function GetAIValue(ByVal portId As Integer) As Single
        If (Not GetAIValueEnable(portId)) Then
            Return 0.0
        End If

        Return Me.AIValue(portId - 1)
    End Function

    ''' <summary>
    ''' 获取AI报警状态是否有效
    ''' </summary>
    ''' <param name="portId">端口号</param>
    Private Function GetAIAlarmEnable(ByVal portId As Integer) As Boolean
        If (portId > 8) Then
            portId = portId - 8
            Return IsTrue(Me.AIAlarmEnable(0), portId)
        Else
            Return IsTrue(Me.AIAlarmEnable(1), portId)
        End If
    End Function

    ''' <summary>
    ''' 获取AI报警
    ''' </summary>
    ''' <param name="portId">端口号</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function GetAlAlarm(ByVal portId As Integer) As PortAlarmEnums
        If (Not GetAIAlarmEnable(portId)) Then
            Return PortAlarmEnums.Unknown
        End If

        If (portId > 8) Then
            portId = portId - 8
            If (IsTrue(Me.AIHighAlarm(1), portId)) Then
                Return PortAlarmEnums.Alarm
            ElseIf (IsTrue(Me.AILowAlarm(1), portId)) Then
                Return PortAlarmEnums.Alarm
            ElseIf (IsTrue(Me.AIHighPreAlarm(1), portId)) Then
                Return PortAlarmEnums.PreAlarm
            ElseIf (IsTrue(Me.AILowPreAlarm(1), portId)) Then
                Return PortAlarmEnums.PreAlarm
            Else
                Return PortAlarmEnums.Normal
            End If
        Else
            If (IsTrue(Me.AIHighAlarm(0), portId)) Then
                Return PortAlarmEnums.Alarm
            ElseIf (IsTrue(Me.AILowAlarm(0), portId)) Then
                Return PortAlarmEnums.Alarm
            ElseIf (IsTrue(Me.AIHighPreAlarm(0), portId)) Then
                Return PortAlarmEnums.PreAlarm
            ElseIf (IsTrue(Me.AILowPreAlarm(0), portId)) Then
                Return PortAlarmEnums.PreAlarm
            Else
                Return PortAlarmEnums.Normal
            End If
        End If
    End Function
End Class